<template>
  <div class="container">
    <h1>人脸表情识别系统</h1>
    
    <!-- 上传区域 -->
    <div class="upload-section">
      <div class="file-input-wrapper">
        <label for="file-upload" class="file-label">
          {{ file ? file.name : '选择图片文件' }}
        </label>
        <input 
          id="file-upload"
          type="file" 
          accept=".jpg,.jpeg,.png" 
          @change="handleFileUpload"
          ref="fileInput"
        >
      </div>
      <button 
        :disabled="!file || isProcessing" 
        @click="analyzeExpression"
        class="analyze-btn"
      >
        <span v-if="isProcessing">
          <i class="spinner"></i> 识别中...
        </span>
        <span v-else>开始识别</span>
      </button>
    </div>

    <!-- 预览区域 -->
    <div v-if="file" class="preview-section">
      <h3>图片预览：</h3>
      <img :src="imagePreview" alt="预览图" class="preview-image">
    </div>

    <!-- 结果展示 -->
    <div v-if="result" class="result-section">
      <div class="result-card">
        <h2>识别结果：<span :class="'emotion-' + resultEmotionCode">{{ result }}</span></h2>
        <div class="result-details">
          <p><strong>置信度：</strong>{{ confidence }}%</p>
          <p><strong>人脸位置：</strong>X: {{ facePosition.x }}, Y: {{ facePosition.y }}</p>
          <p><strong>人脸尺寸：</strong>宽: {{ facePosition.w }}, 高: {{ facePosition.h }}</p>
        </div>
      </div>
    </div>

    <!-- 错误提示 -->
    <div v-if="error" class="error-section">
      <div class="error-card">
        <h3>错误信息：</h3>
        <p>{{ error }}</p>
      </div>
    </div>

    <!-- 日志输出 -->
    <div class="logs-section">
      <div class="logs-header">
        <h3>操作日志</h3>
        <button @click="clearLogs" class="clear-btn">清空日志</button>
      </div>
      <div class="logs-content">
        <div v-for="(log, index) in logs" :key="index" class="log-item">
          <span class="log-time">[{{ log.time }}]</span>
          <span :class="'log-message ' + (log.type === 'error' ? 'error-log' : '')">
            {{ log.message }}
          </span>
        </div>
        <div v-if="logs.length === 0" class="empty-logs">暂无日志记录</div>
      </div>
    </div>
  </div>
</template>

<script setup>
import { ref } from 'vue'
import axios from 'axios'

const EMOTION_MAP = {
  0: '惊讶',
  1: '害怕',
  2: '厌恶',
  3: '高兴',
  4: '悲伤',
  5: '生气',
  6: '正常'
}

// 响应式数据
const file = ref(null)
const imagePreview = ref('')
const result = ref('')
const resultEmotionCode = ref('')
const confidence = ref(0)
const facePosition = ref({ x: 0, y: 0, w: 0, h: 0 })
const rawData = ref('')
const error = ref('')
const isProcessing = ref(false)
const logs = ref([])

// 日志记录方法
const addLog = (message, type = 'info') => {
  logs.value.unshift({
    time: new Date().toLocaleTimeString(),
    message,
    type
  })
  if (logs.value.length > 50) logs.value.pop()
}

const clearLogs = () => {
  logs.value = []
  addLog('日志已清空')
}

// 文件上传处理
const handleFileUpload = (event) => {
  const selectedFile = event.target.files[0]
  if (!selectedFile) return

  const validTypes = ['image/jpeg', 'image/png', 'image/jpg']
  if (!validTypes.includes(selectedFile.type)) {
    error.value = '仅支持 JPG/JPEG/PNG 格式'
    addLog(`无效文件类型：${selectedFile.type}`, 'error')
    return
  }

  // 创建预览图
  const reader = new FileReader()
  reader.onload = (e) => {
    imagePreview.value = e.target.result
  }
  reader.readAsDataURL(selectedFile)

  file.value = selectedFile
  error.value = ''
  result.value = ''
  addLog(`已选择文件：${selectedFile.name} (${(selectedFile.size / 1024).toFixed(2)}KB)`)
}

// 解析表情
const parseFaceResult = (base64Str) => {
  try {
    const decodedStr = atob(base64Str)
    addLog(`Base64解码成功，原始数据长度：${decodedStr.length}`, 'debug')
    
    const jsonData = JSON.parse(decodedStr)
    addLog(`JSON解析成功，检测到 ${jsonData.face_num} 张人脸`, 'debug')
    
    if (jsonData.face_num < 1) {
      throw new Error('未检测到人脸')
    }

    // 获取第一张人脸数据
    const firstFace = jsonData['face_1']
    if (!firstFace?.property) {
      throw new Error('人脸数据格式异常')
    }

    const expression = firstFace.property.expression
    if (typeof expression !== 'number' || expression < 0 || expression > 6) {
      throw new Error(`无效的表情编号：${expression}`)
    }
    
    return {
      emotion: EMOTION_MAP[expression] || '未知表情',
      emotionCode: expression,
      confidence: Math.round(firstFace.score * 100),
      position: {
        x: firstFace.x,
        y: firstFace.y,
        w: firstFace.w,
        h: firstFace.h
      },
      raw: jsonData
    }
  } catch (e) {
    addLog(`解析错误：${e.message}`, 'error')
    throw new Error('表情解析失败: ' + e.message)
  }
}

// 提交分析请求
const analyzeExpression = async () => {
  if (!file.value) return

  isProcessing.value = true
  error.value = ''
  result.value = ''
  rawData.value = ''

  const formData = new FormData()
  formData.append('file', file.value)

  try {
    addLog(`开始上传文件：${file.value.name}`, 'info')
    
    const response = await axios.post('http://localhost:8080/api/face/analyze', formData, {
      headers: { 'Content-Type': 'multipart/form-data' },
      timeout: 10000 // 10秒超时
    })

    addLog(`收到响应，状态码：${response.status}`, 'debug')
    
    const faceResult = response.data?.payload?.face_detect_result
    if (!faceResult?.text) {
      throw new Error('响应缺少有效的text字段')
    }

    // 解析人脸数据
    const parsedResult = parseFaceResult(faceResult.text)
    
    // 更新UI数据
    result.value = parsedResult.emotion
    resultEmotionCode.value = parsedResult.emotionCode
    confidence.value = parsedResult.confidence
    facePosition.value = parsedResult.position
    rawData.value = JSON.stringify(parsedResult.raw, null, 2)
    
    addLog(`识别成功：${result.value} (置信度: ${confidence.value}%)`, 'success')

  } catch (err) {
    error.value = err.response?.data?.message || err.message
    addLog(`请求失败：${error.value}`, 'error')
    
    if (err.response) {
      addLog(`响应数据：${JSON.stringify(err.response.data)}`, 'debug')
    }
  } finally {
    isProcessing.value = false
  }
}
</script>

<style scoped>
.container {
  max-width: 900px;
  margin: 2rem auto;
  padding: 20px;
  font-family: 'Arial', sans-serif;
}

h1 {
  text-align: center;
  color: #2c3e50;
  margin-bottom: 2rem;
}

.upload-section {
  background: #f8f9fa;
  border-radius: 8px;
  padding: 2rem;
  margin-bottom: 2rem;
  text-align: center;
  box-shadow: 0 2px 4px rgba(0,0,0,0.1);
}

.file-input-wrapper {
  margin-bottom: 1rem;
}

.file-label {
  display: inline-block;
  padding: 10px 15px;
  background: #e9ecef;
  border-radius: 4px;
  cursor: pointer;
  transition: all 0.3s;
}

.file-label:hover {
  background: #dee2e6;
}

input[type="file"] {
  display: none;
}

.analyze-btn {
  background: #42b983;
  color: white;
  border: none;
  padding: 12px 24px;
  border-radius: 4px;
  font-size: 16px;
  cursor: pointer;
  transition: all 0.3s;
  display: inline-flex;
  align-items: center;
  justify-content: center;
}

.analyze-btn:disabled {
  background: #cccccc;
  cursor: not-allowed;
}

.analyze-btn:hover:not(:disabled) {
  background: #3aa876;
}

.spinner {
  display: inline-block;
  width: 16px;
  height: 16px;
  border: 2px solid rgba(255,255,255,0.3);
  border-radius: 50%;
  border-top-color: #fff;
  animation: spin 1s ease-in-out infinite;
  margin-right: 8px;
}

@keyframes spin {
  to { transform: rotate(360deg); }
}

.preview-section {
  margin: 2rem 0;
  text-align: center;
}

.preview-image {
  max-width: 100%;
  max-height: 300px;
  border-radius: 8px;
  box-shadow: 0 4px 8px rgba(0,0,0,0.1);
}

.result-section {
  margin: 2rem 0;
}

.result-card {
  background: #f8f9fa;
  border-radius: 8px;
  padding: 1.5rem;
  box-shadow: 0 2px 4px rgba(0,0,0,0.1);
}

.result-card h2 {
  margin-top: 0;
  color: #2c3e50;
}

.emotion-0 { color: #ff7675; } /* 惊讶 */
.emotion-1 { color: #a29bfe; } /* 害怕 */
.emotion-2 { color: #55efc4; } /* 厌恶 */
.emotion-3 { color: #fdcb6e; } /* 高兴 */
.emotion-4 { color: #74b9ff; } /* 悲伤 */
.emotion-5 { color: #e17055; } /* 生气 */
.emotion-6 { color: #636e72; } /* 正常 */

.result-details {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: 1rem;
  margin-top: 1rem;
}

.error-section {
  margin: 2rem 0;
}

.error-card {
  background: #ffecec;
  border-left: 4px solid #ff6b6b;
  padding: 1rem;
  border-radius: 4px;
}

.error-card h3 {
  margin-top: 0;
  color: #ff6b6b;
}

.logs-section {
  margin-top: 3rem;
  background: #f8f9fa;
  border-radius: 8px;
  overflow: hidden;
  box-shadow: 0 2px 4px rgba(0,0,0,0.1);
}

.logs-header {
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding: 1rem 1.5rem;
  background: #e9ecef;
  border-bottom: 1px solid #dee2e6;
}

.logs-header h3 {
  margin: 0;
}

.clear-btn {
  background: none;
  border: 1px solid #adb5bd;
  padding: 4px 8px;
  border-radius: 4px;
  cursor: pointer;
  font-size: 12px;
}

.clear-btn:hover {
  background: #dee2e6;
}

.logs-content {
  max-height: 300px;
  overflow-y: auto;
  padding: 1rem;
}

.log-item {
  padding: 8px 0;
  border-bottom: 1px solid #eee;
  font-family: monospace;
  font-size: 14px;
}

.log-time {
  color: #6c757d;
  margin-right: 10px;
}

.error-log {
  color: #dc3545;
}

.empty-logs {
  color: #6c757d;
  text-align: center;
  padding: 1rem;
}
</style>